package com.macro.mall.portal.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.*;
import com.macro.mall.portal.dto.OssCallbackParam;
import com.macro.mall.portal.dto.OssCallbackResult;
import com.macro.mall.portal.dto.OssPolicyResult;
import com.macro.mall.portal.service.OssService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Oss对象存储管理Service实现类
 * Created by macro on 2018/5/17.
 */
@Service
@Slf4j
public class OssServiceImpl implements OssService {

	private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class);
	@Value("${aliyun.oss.policy.expire}")
	private int ALIYUN_OSS_EXPIRE;
	@Value("${aliyun.oss.maxSize}")
	private int ALIYUN_OSS_MAX_SIZE;
	@Value("${aliyun.oss.callback}")
	private String ALIYUN_OSS_CALLBACK;
	@Value("${aliyun.oss.bucketName}")
	private String ALIYUN_OSS_BUCKET_NAME;
	@Value("${aliyun.oss.endpoint}")
	private String ALIYUN_OSS_ENDPOINT;
	@Value("${aliyun.oss.dir.prefix}")
	private String ALIYUN_OSS_DIR_PREFIX;

	@Autowired
	private OSSClient ossClient;

	public static final String HTTPS = "https://";
	public static final String DOT = ".";
	public static final String FORWARD_SLASH = "/";


	/**
	 * 上传文件
	 * @param file 上传的文件
	 * @return 可访问的路径
	 */
	@Override
	public String upload(MultipartFile file) throws Exception{

		String extend =file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
		// 获取文件名称
		String sourceName = DateUtil.current() + extend;
		// 获取地域节点
		String endpoint = ALIYUN_OSS_ENDPOINT;
		// 获取存储空间名称
		String bucketName = ALIYUN_OSS_BUCKET_NAME;
		// 当前日期
		String ymd = DateUtil.format(new Date(),"yyyyMMdd");
		// 文件存放地址（不带 bucket）
		String filePath = ALIYUN_OSS_DIR_PREFIX + ymd+ FORWARD_SLASH + sourceName;

		// 访问路径：https://bucket.endpoint/filePath
		String urlPath = HTTPS + bucketName + DOT +  endpoint + FORWARD_SLASH + filePath;
		// 相对路径
		String relativePath = FORWARD_SLASH + filePath;


		try {
			// 判断 bucket 是否存在
			if (!ossClient.doesBucketExist(bucketName)) {
				throw new Exception("存储空间不存在");
			}
			// 创建PutObjectRequest对象。
			PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, filePath, file.getInputStream());
			ObjectMetadata metadata = new ObjectMetadata();

			/*
			 * 指定存储类型：
			 *      对于任意存储类型的Bucket，如果上传Object时指定此参数，则此次上传的Object将存储为指定的类型
			 * 取值：
			 *      Standard：标准存储
			 *      IA：低频访问
			 *      Archive：归档存储
			 *      ColdArchive：冷归档存储
			 *      DeepColdArchive：深度冷归档存储
			 */

			// 设置存储类型：标准存储（默认标准存储）
			metadata.setHeader(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());

			/*
			 * 指定上传文件的访问权限：
			 * 取值：
			 *      default（默认）：Object遵循所在存储空间的访问权限
			 *      private：私有
			 *      public-read：公共读
			 *      public-read-write：公共读写
			 */

			// 设置访问权限：默认（遵循所在存储空间的访问权限）
			metadata.setObjectAcl(CannedAccessControlList.Default);

			/*
			 * 指定上传文件操作时是否覆盖同名 Object：
			 *      不指定 x-oss-forbid-overwrite 时，默认覆盖同名 Object
			 *      指定 x-oss-forbid-overwrite 为 false 时，表示允许覆盖同名 Object
			 *      指定 x-oss-forbid-overwrite 为 true 时，表示禁止覆盖同名 Object，如果同名 Object 已存在，程序将报错
			 */

			// 设置禁止覆盖同名文件
			metadata.setHeader("x-oss-forbid-overwrite", "false");

			// 设置元数据
			putObjectRequest.setMetadata(metadata);

			// 上传文件
			ossClient.putObject(putObjectRequest);

		} catch (IOException e) {
			log.error("failed to upload file.detail message:{}", e.getMessage());
		} catch (OSSException e) {
			log.error("failed to upload file.detail message:{}", e.getMessage());
		}
		return urlPath;
	}


	/**
	 * 签名生成
	 */
	@Override
	public OssPolicyResult policy() {
		OssPolicyResult result = new OssPolicyResult();
		// 存储目录
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		String dir = ALIYUN_OSS_DIR_PREFIX+sdf.format(new Date());
		// 签名有效期
		long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
		Date expiration = new Date(expireEndTime);
		// 文件大小
		long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;
		// 回调
		OssCallbackParam callback = new OssCallbackParam();
		callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
		callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
		callback.setCallbackBodyType("application/x-www-form-urlencoded");
		// 提交节点
		String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
		try {
			PolicyConditions policyConds = new PolicyConditions();
			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
			policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
			String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
			byte[] binaryData = postPolicy.getBytes("utf-8");
			String policy = BinaryUtil.toBase64String(binaryData);
			String signature = ossClient.calculatePostSignature(postPolicy);
			String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8"));
			// 返回结果
			result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
			result.setPolicy(policy);
			result.setSignature(signature);
			result.setDir(dir);
			result.setCallback(callbackData);
			result.setHost(action);
		} catch (Exception e) {
			LOGGER.error("签名生成失败", e);
		}
		return result;
	}

	@Override
	public OssCallbackResult callback(HttpServletRequest request) {
		OssCallbackResult result= new OssCallbackResult();
		String filename = request.getParameter("filename");
		filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename);
		result.setFilename(filename);
		result.setSize(request.getParameter("size"));
		result.setMimeType(request.getParameter("mimeType"));
		result.setWidth(request.getParameter("width"));
		result.setHeight(request.getParameter("height"));
		return result;
	}
}
